package com.ztom.agent.asm;

import com.ztom.agent.config.TraceConfig;
import com.ztom.agent.config.TraceFilter;
import com.ztom.agent.log.Logger;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static org.objectweb.asm.Opcodes.*;

/**
 * @author ZhangTao
 */
public class TraceClassVisitor extends ClassVisitor {

    private final String innerClassName;

    private boolean isInterface;

    private List<String> fieldNameList = new ArrayList<>();

    public TraceClassVisitor(final ClassVisitor cv, String innerClassName) {
        super(ASM5, cv);
        this.innerClassName = innerClassName;
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        Logger.debug("TraceClassVisitor.visit([" + version + "], [" + access + "], [" + name + "], [" + signature + "], [" + superName + "], [" + Arrays.toString(interfaces) + "])");

        super.visit(version, access, name, signature, superName, interfaces);
        this.isInterface = (access & ACC_INTERFACE) != 0;
    }

    @Override
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
        // 将字段的首字母大写
        String upFieldName = name.substring(0, 1).toUpperCase() + name.substring(1);
        // 记录字段的get set 和 is方法
        fieldNameList.add("get" + upFieldName);
        fieldNameList.add("set" + upFieldName);
        fieldNameList.add("is" + upFieldName);

        return super.visitField(access, name, desc, signature, value);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        // 接口和不需要注入的方法 直接略过
        if (isInterface || !isNeedVisit(access, name)) {
            return super.visitMethod(access, name, desc, signature, exceptions);
        }

        MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
        if (mv == null) {
            return null;
        }
        Logger.debug("TraceClassVisitor.visitMethod([" + access + "], [" + name + "], [" + desc + "], [" + signature + "], [" + Arrays.toString(exceptions) + "]), innerClassName=" + innerClassName);

        return new TraceMethodVisitor(mv, access, name, desc, innerClassName);
    }

    private boolean isNeedVisit(int access, String name) {
        // 不对私有方法进行注入, 默认值需要
        if ((access & ACC_PRIVATE) != 0 && TraceConfig.getInstance().isExcludePrivateMethod()) {
            return false;
        }

        // 不对抽象方法、native方法、桥接方法、编译器生成的方法进行注入
        if ((access & ACC_ABSTRACT) != 0
                || (access & ACC_NATIVE) != 0
                || (access & ACC_BRIDGE) != 0
                || (access & ACC_SYNTHETIC) != 0) {
            return false;
        }

        // 不对构造方法和静态代码块进行注入
        if ("<init>".equals(name) || "<clinit>".equals(name)) {
            return false;
        }

        // 不对字段的get set和is方法以及不需要注入的方法进行注入
        if (fieldNameList.contains(name) || TraceFilter.isNotNeedInjectMethod(name)) {
            return false;
        }

        return true;
    }
}
